using System.Web.Caching;
using System;
using System.Web;
using System.Collections.Generic;
namespace Utils
{
    public class SimpleCache
    {
        public static void Delete(string key)
        {
            if (HttpRuntime.Cache != null)
                HttpRuntime.Cache.Remove(key);
        }
    }
    public class SimpleCache<T> where T : class
    {
        public delegate T DataRetrievalClosure();
        public static T Get(string key, DataRetrievalClosure getData)
        {
            return Get(key, getData, DateTime.Now.AddMinutes( 15));
        }
        /// <summary>
        ///This method replaces the cache checking, locking, rechecking, retrieving and updating logic we normally have to do inside each cached method
        /// </summary>
        /// <param name="key">Cache key</param>
        /// <param name="getData">Delegate/closure that, when called will retrieve the data to be cached; only called in case of a cache miss</param>
        /// <param name="expiration">optional absolute expiration time</param>
        /// <returns></returns>
        public static T Get(string key, DataRetrievalClosure getData, DateTime expiration)
        {
            return Get(key, getData, expiration, Cache.NoSlidingExpiration, null);
        }
        /// <summary>
        ///This method replaces the cache checking, locking, rechecking, retrieving and updating logic we normally have to do inside each cached method
        /// </summary>
        /// <param name="key">Cache key</param>
        /// <param name="getData">Delegate/closure that, when called, will retrieve the data to be cached; only called in case of a cache miss</param>
        /// <param name="dependencies"></param>
        /// <param name="expiration"></param>
        /// <param name="slidingExpiration">Default = Cache.NoSlidingExpiration</param>
        /// <returns></returns>
        private static T Get(string key, DataRetrievalClosure getData, DateTime expiration, TimeSpan slidingExpiration, CacheDependency dependencies)
        {
            T result = HttpRuntime.Cache[key] as T; 
            
            if (result == null)
            {
                //We need to lock to avoid multiple simultaneous retrievals (by multiple threads) immediately after a cache miss.
                //the cache key is a good object to lock on because:
                //it is a string; when interned it will always be a reference to the same instance
                //(unique per each cache key / cached object which is exactly what we want)
                object lockObject = string.Intern( key );
                
                lock (lockObject)
                {
                    result = (T)HttpRuntime.Cache[key];
                    if (result == null)
                    {
                        result = (T)getData();
                        if (result!=null)
                            HttpRuntime.Cache.Insert(key, result, dependencies, expiration, slidingExpiration);
                    }
                }
            }

            return result;
        }
    }
}